﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Acms.Bll.Tests
{
    [TestClass]
    public class UsersTest : Base
    {
        public void ValidateList(List<TestData.User> expected, List<Bll.User> actual)
        {
            Assert.AreEqual(expected.Count, actual.Count);
            foreach (var item in expected)
            {
                ValidateProperties(item, actual[expected.IndexOf(item)]);
            }
        }


        public void ValidateProperties(Bll.User expected, Bll.User actual)
        {
            Assert.AreEqual(expected.Address1, actual.Address1);
            Assert.AreEqual(expected.Address2, actual.Address2);
            Assert.AreEqual(expected.Age, actual.Age);
            AssertHelper.AreEqual((byte[])expected.ConcurencyId, (byte[])actual.ConcurencyId);
            Assert.AreEqual(expected.County, actual.County);
            AssertHelper.AreEqual(expected.Created, actual.Created, false);
            Assert.AreEqual(expected.CustomBool1, actual.CustomBool1);
            Assert.AreEqual(expected.CustomBool2, actual.CustomBool2);
            Assert.AreEqual(expected.CustomBool3, actual.CustomBool3);
            Assert.AreEqual(expected.CustomInt1, actual.CustomInt1);
            Assert.AreEqual(expected.CustomInt2, actual.CustomInt2);
            Assert.AreEqual(expected.CustomInt3, actual.CustomInt3);
            Assert.AreEqual(expected.CustomString1, actual.CustomString1);
            Assert.AreEqual(expected.CustomString2, actual.CustomString2);
            Assert.AreEqual(expected.CustomString3, actual.CustomString3);
            Assert.AreEqual(expected.DateOfBirth, actual.DateOfBirth);
            Assert.AreEqual(expected.DisplayName, actual.DisplayName);
            Assert.AreEqual(expected.EmailAddress, actual.EmailAddress);
            Assert.AreEqual(expected.EmailAuthenticated, actual.EmailAuthenticated);
            Assert.AreEqual(expected.FirstName, actual.FirstName);
            Assert.AreEqual(expected.Gender, actual.Gender);
            Assert.AreEqual(expected.Id, actual.Id);
            Assert.AreEqual(expected.IsNewRecord, actual.IsNewRecord);
            AssertHelper.AreEqual(expected.LastActivity, actual.LastActivity);
            AssertHelper.AreEqual(expected.LastChangedPassword, actual.LastChangedPassword);
            AssertHelper.AreEqual(expected.LastLocked, actual.LastLocked);
            AssertHelper.AreEqual(expected.LastLogin, actual.LastLogin);
            Assert.AreEqual(expected.LastName, actual.LastName);
            Assert.AreEqual(expected.Locked, actual.Locked);
            Assert.AreEqual(expected.MobileTel, actual.MobileTel);
            Assert.AreEqual(expected.PostCode, actual.PostCode);
            Assert.AreEqual(expected.SiteId, actual.SiteId);
            Assert.AreEqual(expected.Town, actual.Town);
            Assert.AreEqual(expected.UserName, actual.UserName);
        }

        public void ValidateProperties(TestData.User expected, Bll.User actual)
        {
            Assert.AreEqual(expected.Address1, actual.Address1);
            Assert.AreEqual(expected.Address2, actual.Address2);
            Assert.AreEqual(expected.County, actual.County);
            AssertHelper.AreEqual(expected.Created.ToLocalTime(), actual.Created);
            Assert.AreEqual(expected.CustomBool1, actual.CustomBool1);
            Assert.AreEqual(expected.CustomBool2, actual.CustomBool2);
            Assert.AreEqual(expected.CustomBool3, actual.CustomBool3);
            Assert.AreEqual(expected.CustomInt1, actual.CustomInt1);
            Assert.AreEqual(expected.CustomInt2, actual.CustomInt2);
            Assert.AreEqual(expected.CustomInt3, actual.CustomInt3);
            Assert.AreEqual(expected.CustomString1, actual.CustomString1);
            Assert.AreEqual(expected.CustomString2, actual.CustomString2);
            Assert.AreEqual(expected.CustomString3, actual.CustomString3);
            AssertHelper.AreEqual(expected.DateOfBirth.ToLocalTime(), actual.DateOfBirth);
            Assert.AreEqual(expected.EmailAddress, actual.EmailAddress);
            Assert.AreEqual(expected.FirstName, actual.FirstName);
            Assert.AreEqual((Bll.User.Genders)expected.Gender, actual.Gender);
            Assert.AreEqual(expected.Id, actual.Id);
            Assert.AreEqual(expected.LastName, actual.LastName);
            Assert.AreEqual(expected.MobileTel, actual.MobileTel);
            Assert.AreEqual(expected.PostCode, actual.PostCode);
            Assert.AreEqual(expected.SiteId, actual.SiteId);
            Assert.AreEqual(expected.Town, actual.Town);
            Assert.AreEqual(expected.UserName, actual.UserName);
            Assert.AreEqual(expected.VersionId, (byte[])actual.ConcurencyId);
        }

        [TestMethod]
        public void User_GetById_ReturnsNull_WithIncorrectId()
        {
            Assert.IsNull(Bll.User.GetById(int.MaxValue));
        }

        [TestMethod]
        public void User_GetById_ReturnsNotNull_WithCorrectId()
        {
            var tmp = Bll.User.GetById(_TestUsers[0].Id);
            Assert.IsNotNull(tmp);
        }

        [TestMethod]
        public void User_Search_ReturnsEmptyList_WithIncorrectSiteId()
        {
            Assert.AreEqual(0, Bll.User.Search(int.MaxValue).Count());
        }

        [TestMethod]
        public void User_Search_ReturnsPopulatedList_WithCorrectSiteId()
        {
            Assert.AreEqual(_TestUsers.Count(), Bll.User.Search(_TestSites[0].Id).Count());
        }

        [TestMethod]
        public void User_GetById_ReturnsCorrectProperties()
        {
            var tmp = Bll.User.GetById(_TestUsers[0].Id);
            Assert.IsNotNull(tmp);
            ValidateProperties(_TestUsers[0], tmp);
        }

        [TestMethod]
        public void User_GetAllBySiteId_ReturnsCorrectProperties()
        {
            var tmp = Bll.User.Search(_TestSites[0].Id);
            Assert.IsNotNull(tmp);
            ValidateList(_TestUsers.ToList(), tmp);
        }

        [TestMethod]
        public void User_Insert()
        {
            var tmp = Bll.User.CreateInstance(_TestSites[0].Id);

            tmp.Address1 = "InsertAddress1";
            tmp.Address2 = "InsertAddress2";
            tmp.County = "InsertCounty";
            tmp.CustomBool1 = false;
            tmp.CustomBool2 = true;
            tmp.CustomBool3 = false;
            tmp.CustomInt1 = 10;
            tmp.CustomInt2 = 11;
            tmp.CustomInt3 = 12;
            tmp.CustomString1 = "InsertCustom1";
            tmp.CustomString2 = "InsertCustom2";
            tmp.CustomString3 = "InsertCustom3";
            tmp.DateOfBirth = new DateTime(1980, 4, 1);
            tmp.EmailAddress = "insert@email.com";
            tmp.FirstName = "InsertFirstName";
            tmp.Gender = Acms.Bll.User.Genders.Female;
            tmp.LastName = "InsertLastName";
            tmp.MobileTel = "0902 212 41242";
            tmp.PostCode = "W9 3DF";
            tmp.Town = "InsertTown";
            tmp.UserName = "InsertUserName";

            tmp.SetPassword(null, "InsertPassword");

            DateTime expectedDateTime = DateTime.Now;
            tmp.Update();

            Assert.IsTrue(tmp.Id > 0);
            var expected = _Context.Users.Where(x => x.Id == tmp.Id).Single();
            //ValidateProperties(expected, tmp);
            var tmp2 = Bll.User.GetById(tmp.Id);
            ValidateProperties(tmp2, tmp);
        }

        [TestMethod]
        public void User_Update()
        {
            var tmp = Bll.User.GetById(_TestUsers[0].Id);
            
            Assert.IsNotNull(tmp);

            tmp.Address1 = "UpdateAddress1";
            tmp.Address2 = "UpdateAddress2";
            tmp.County = "UpdateCounty";
            tmp.CustomBool1 = false;
            tmp.CustomBool2 = true;
            tmp.CustomBool3 = false;
            tmp.CustomInt1 = 10;
            tmp.CustomInt2 = 11;
            tmp.CustomInt3 = 12;
            tmp.CustomString1 = "UpdateCustom1";
            tmp.CustomString2 = "UpdateCustom2";
            tmp.CustomString3 = "UpdateCustom3";
            tmp.DateOfBirth = new DateTime(1980, 4, 1);
            tmp.EmailAddress = "Update@email.com";
            tmp.FirstName = "UpdateFirstName";
            tmp.Gender = Acms.Bll.User.Genders.Female;
            tmp.LastName = "UpdateLastName";
            tmp.MobileTel = "0902 212 41242";
            tmp.PostCode = "W9 3DF";
            tmp.Town = "UpdateTown";
            tmp.UserName = "UpdateUserName";

            tmp.Update();

            var expected = _FreshContext.Users.Where(x => x.Id == tmp.Id).Single();

            ValidateProperties(expected, tmp);
        }

        [TestMethod]
        public void User_Delete()
        {
            var tmp = Bll.User.GetById(_TestUsers[0].Id);
            Assert.IsNotNull(tmp);
            tmp.Delete();
            Assert.AreEqual(_Context.Users.Where(x => !x.Deleted).Count(), Bll.User.Search(_TestSites[0].Id).Count());
            tmp = Bll.User.GetById(_TestUsers[0].Id);
            Assert.IsNull(tmp);
        }

        [TestMethod]
        public void User_AuthenticatePassword_WithInCorrectPassword_ReturnsFalse()
        {
            var tmp = Bll.User.GetById(_TestUsers[0].Id);
            Assert.IsNotNull(tmp);
            Assert.IsFalse(tmp.AuthenticatePassword("Rubbish"));
        }

        [TestMethod]
        public void User_AuthenticatePassword_WithCorrectPassword_ReturnsTrue()
        {
            var tmp = Bll.User.GetById(_TestUsers[0].Id);
            Assert.IsTrue(tmp.AuthenticatePassword("Password0"));
        }

        [TestMethod]
        public void User_SetPassword_WithInCorrectOriginalPassword_ReturnsFalse()
        {
            var tmp = Bll.User.GetById(_TestUsers[0].Id);
            Assert.IsFalse(tmp.SetPassword("PasswordRubbish", "NewPassword"));
        }

        [TestMethod]
        public void User_SetPassword_WithCorrectOriginalPassword_ReturnsTrueAndCreatedNewSaltAndNewEncodedPassword()
        {
            //create a new context to ensure not cached
            DBDataContext tmpContext = new DBDataContext();
            int originalSalt = _TestUsers[0].Salt;
            var tmp = Bll.User.GetById(_TestUsers[0].Id);
            Assert.IsTrue(tmp.SetPassword("Password0", "NewPassword"));
            Assert.IsTrue(tmp.AuthenticatePassword("NewPassword"));
            tmp.Update();
            //check salt has been changed
            Assert.AreNotEqual(originalSalt, tmpContext.Users.Where(x => x.Id == _TestUsers[0].Id).Single().Salt);
        }

        [TestMethod]
        public void User_NewUser_EmailAuthenticate_IsFalse()
        {
            var tmp = Bll.User.CreateInstance(_TestSites[0].Id);
            Assert.IsFalse(tmp.EmailAuthenticated);
        }

        [TestMethod]
        public void User_GetByEmailActivationCodeAndSiteId_WithIncorrectCode_ReturnsNull()
        {
            Assert.IsNull(User.GetByEmailActivationCodeAndSiteId("Rubbish", _TestUsers[0].SiteId.Value));
        }

        [TestMethod]
        public void User_GetByEmailActivationCodeAndSiteId_WithIncorrectSiteId_ReturnsNull()
        {
            Assert.IsNull(User.GetByEmailActivationCodeAndSiteId(_TestUsers[0].EmailActivationCodes[0].Code, int.MaxValue));
        }

        [TestMethod]
        public void User_GetByEmailActivationCodeAndSiteId_WithCorrectArguments_PopulatesCorrectly()
        {
            var tmp = User.GetByEmailActivationCodeAndSiteId(_TestUsers[0].EmailActivationCodes[0].Code, _TestUsers[0].SiteId.Value);
            ValidateProperties(_TestUsers[0], tmp);
        }

        /*[TestMethod]
        [ExpectedException(typeof(ValidationException))]
        public void User_ValdiateWithEmptyEmailAddress_ThrowsException()
        {
            UserFactory factory = new UserFactory(_Context);
            var tmp = new User(_TestSite.Id);
            tmp.Anon = true;
            tmp.Comment = "test";
            tmp.CountryId = 1;
            tmp.Department = "test";
            tmp.SetPassword(null, "Password1");
            tmp.State = "test";
            tmp.ThumnailId = 55;
            factory.Save(tmp);
        }*/

        /*
        [TestMethod]
        [ExpectedException(typeof(ValidationException))]
        public void User_ValdiateWithDuplicateEmailAddressInDB_ThrowsException()
        {
            UserFactory factory = new UserFactory(_Context);
            var tmp = new User(_TestSite.Id);
            tmp.Anon = true;
            tmp.Comment = "test";
            tmp.CountryId = 1;
            tmp.Department = "test";
            tmp.SetPassword(null, "Password1");
            tmp.State = "test";
            tmp.ThumnailId = 55;
            tmp.FirstName = "test";
            tmp.LastName = "test";
            tmp.EmailAddress = "testValidateion@email.com";
            try
            {
                factory.Save(tmp);
            }
            catch (ValidationException)
            {
                throw new AssertInconclusiveException("Expected Excecptions To Be Thrown after second save");
            }
            var tmp2 = new User(_TestSite.Id);
            tmp2.Anon = true;
            tmp2.Comment = "test";
            tmp2.CountryId = 1;
            tmp2.Department = "test";
            tmp2.SetPassword(null, "Password1");
            tmp2.State = "test";
            tmp2.ThumnailId = 55;
            tmp2.FirstName = "test";
            tmp2.LastName = "test";
            tmp2.EmailAddress = "testValidateion@email.com";
            factory.Save(tmp2);
        }*/
        /*
        [TestMethod]
        [ExpectedException(typeof(ValidationException))]
        public void User_ValdiateWithDuplicateEmailAddressInUpdateing_ThrowsException()
        {
            UserFactory factory = new UserFactory(_Context);
            var tmp = new User(_TestSite.Id);
            tmp.Anon = true;
            tmp.Comment = "test";
            tmp.CountryId = 1;
            tmp.Department = "test";
            tmp.SetPassword(null, "Password1");
            tmp.State = "test";
            tmp.ThumnailId = 55;
            tmp.FirstName = "test";
            tmp.LastName = "test";
            tmp.EmailAddress = "testValidateion@email.com";
            var tmp2 = new User(_TestSite.Id);
            tmp2.Anon = true;
            tmp2.Comment = "test";
            tmp2.CountryId = 1;
            tmp2.Department = "test";
            tmp2.SetPassword(null, "Password1");
            tmp2.State = "test";
            tmp2.ThumnailId = 55;
            tmp2.FirstName = "test";
            tmp2.LastName = "test";
            tmp2.EmailAddress = "testValidateion@email.com";
            factory.Save(new User[] { tmp, tmp2 });
        }*/
        /*
        [TestMethod]
        [ExpectedException(typeof(ValidationException))]
        public void User_ValidateWithFirstNameNull_ThrowsException()
        {
            UserFactory factory = new UserFactory(_Context);
            User tmp = new User(_TestSite.Id);
            tmp.Anon = true;
            tmp.Comment = "test";
            tmp.CountryId = 1;
            tmp.Department = "test";
            tmp.SetPassword(null, "Password1");
            tmp.State = "test";
            tmp.ThumnailId = 55;
            tmp.LastName = "test";
            tmp.EmailAddress = "testValidateion@email.com";

            tmp.FirstName = "";
            factory.Save(tmp);
        }*/
        /*
        [TestMethod]
        [ExpectedException(typeof(ValidationException))]
        public void User_ValidateWithLastNameNull_ThrowsException()
        {
            UserFactory factory = new UserFactory(_Context);
            User tmp = new User(_TestSite.Id);
            tmp.EmailAddress = "testValidateion@email.com";
            tmp.Anon = true;
            tmp.Comment = "test";
            tmp.CountryId = 1;
            tmp.Department = "test";
            tmp.SetPassword(null, "Password1");
            tmp.State = "test";
            tmp.ThumnailId = 55;
            tmp.FirstName = "test";
            tmp.LastName = "";

            factory.Save(tmp);
        }*/
        /*
        [TestMethod]
        public void User_CountryPropertyReturnsCorrectCountryId()
        {
            UserFactory factory = new UserFactory(_Context);
            var user = factory.GetById(_TestUsers[0].Id);
            Assert.IsNotNull(user);
            Assert.IsNotNull(user.Country);
            Assert.AreEqual(user.CountryId, user.Country.Id);
            Assert.AreEqual("United Kingdom", user.Country.Name);
            Assert.AreEqual(DateTime.Parse("24/10/1975 12:00:00"), user.Country.Created);
            Assert.AreEqual(user.SiteId, user.Country.SiteId);
        }*/
        /*
        [TestMethod]
        public void User_SetCountryProperty_UpdatesCorrectly()
        {
            UserFactory factory = new UserFactory(_Context);
            var user = factory.GetById(_TestUsers[0].Id);
            Assert.IsNotNull(user);
            Assert.IsNotNull(user.Country);
            Assert.AreEqual(_TestCountries[0].Id, user.Country.Id);

            CountryFactory countries = new CountryFactory(_Context);
            user.Country = countries.GetById(_TestCountries[1].Id);

            Assert.AreEqual(_TestCountries[1].Id, user.Country.Id);
            Assert.AreEqual(_TestCountries[1].Id, user.CountryId);

            factory.Save(user);

            Assert.AreEqual(_TestCountries[1].Id, user.Country.Id);
            Assert.AreEqual(_TestCountries[1].Id, user.CountryId);

            user = factory.GetById(_TestUsers[0].Id);

            Assert.AreEqual(_TestCountries[1].Id, user.Country.Id);
            Assert.AreEqual(_TestCountries[1].Id, user.CountryId);
        }*/
        /*
        [TestMethod]
        [ExpectedException(typeof(Bll.Exceptions.ValidationException))]
        public void User_ValidationFails_WhenEmailAdressHasNoValidMatchInEmailRestrictions()
        {
            //reset first counry id to use in this test only
            _TestCountries[0].SiteId = _AlternateSite.Id;
            _Context.SubmitChanges();

            EmailRestrictionFactory restrictionFactory = new EmailRestrictionFactory(_Context);
            UserFactory userFactory = new UserFactory(_Context);

            EmailRestriction restriction = new EmailRestriction(_AlternateSite.Id);
            restriction.EmailFilter = "[a-zA-Z0-9._]{0,}@testMail.com";
            restrictionFactory.Save(restriction);

            User tmp = new User(_AlternateSite.Id);
            tmp.EmailAddress = "hello@rubbish.com";
            tmp.FirstName = "First";
            tmp.LastName = "Last";
            tmp.CountryId = _TestCountries[0].Id;
            tmp.SetPassword(null, "TestPassword");
            userFactory.Save(tmp);
        }*/
        /*
        [TestMethod]
        public void User_ValidationPasses_WhenEmailAdressHasValidMatchInEmailRestrictions()
        {
            //reset first counry id to use in this test only
            _TestCountries[0].SiteId = _AlternateSite.Id;
            _Context.SubmitChanges();

            EmailRestrictionFactory restrictionFactory = new EmailRestrictionFactory(_Context);
            UserFactory userFactory = new UserFactory(_Context);

            EmailRestriction restriction = new EmailRestriction(_AlternateSite.Id);
            restriction.EmailFilter = "[a-zA-Z0-9._]{0,}@testMail.com";
            restriction.Access = true;
            restrictionFactory.Save(restriction);

            User tmp = new User(_AlternateSite.Id);
            tmp.EmailAddress = "hello@testMail.com";
            tmp.FirstName = "First";
            tmp.LastName = "Last";
            tmp.CountryId = _TestCountries[0].Id;
            tmp.SetPassword(null, "TestPassword");
            userFactory.Save(tmp);
        }*/
        
        [TestMethod]
        public void User_GetByEmailAddress_WithCorrectSiteIdAndEmail_ReturnsNotNull()
        {
            var tmp = Bll.User.GetByEmailAddress(_TestUsers[0].EmailAddress, _TestSites[0].Id);
            Assert.IsNotNull(tmp);
            ValidateProperties(_TestUsers[0], tmp);
        }
        /*
        [TestMethod]
        public void User_GetByEmailAddress_WithCorrectSuperUsersEmail_ReturnsNotNull()
        {
            Assert.IsNotNull(factory.GetByEmailAddress(_TestSite.Id, _TestSuperUsers[0].EmailAddress));
            Assert.IsNotNull(factory.GetByEmailAddress(_AlternateSite.Id, _TestSuperUsers[0].EmailAddress));
        }*/

        [TestMethod]
        public void User_GetByEmailAddress_WithIncorrectSiteId_ReturnsNull()
        {
            Assert.IsNull(Bll.User.GetByEmailAddress(_TestUsers[0].EmailAddress, _TestSites[1].Id));
        }

        [TestMethod]
        public void User_GetByEmailAddress_WithIncorrectEmail_ReturnsNull()
        {
            Assert.IsNull(Bll.User.GetByEmailAddress(string.Concat("rubbish", _TestUsers[0].EmailAddress), _TestSites[0].Id));
        }

        /*[TestMethod]
        public void User_GetLoginKey_ForUserWithExistingLoginKey_ReturnsGuid()
        {
            var tmp = Bll.User.GetById(_TestUsers[0].Id);
            Assert.IsNotNull(tmp);
            Assert.AreEqual(_TestUserLoginKeys.Where(x => x.UserId == tmp.Id).First().Key, tmp.GetLoginKey(false));
        }*/
        /*
        [TestMethod]
        public void User_GetLoginKey_ForUserWithOutExistingLoginKey_ReturnsGuid()
        {
            //create new user with no loing keys
            Dal.User tmpDalUser = new Dal.User();
            tmpDalUser.Anon = false;
            tmpDalUser.CountryId = _TestCountries[0].Id;
            tmpDalUser.Created = DateTime.UtcNow;
            tmpDalUser.EmailAddress = "allow@man.co.uk";
            tmpDalUser.FirstName = "";
            tmpDalUser.LastName = "";
            tmpDalUser.Password = "";

            _Context.Users.InsertOnSubmit(tmpDalUser);
            _Context.SubmitChanges();
            Assert.IsTrue(tmpDalUser.Id > 0);

            //Get bll user
            UserFactory factory = new UserFactory(_Context);
            User tmp = factory.GetById(tmpDalUser.Id);
            Assert.IsNotNull(tmp);

            Guid tmpKey = factory.GetLoginKey(tmp);
            Assert.IsNotNull(tmpKey);
            Assert.AreNotEqual(Guid.Empty, tmpKey);
            Assert.AreEqual(tmpKey, factory.GetLoginKey(tmp));
        }*/

        /*[TestMethod]
        public void User_GetByLoginKey_WithIncorrectKey_ReturnsNull()
        {
            Assert.IsNull(Bll.User.GetByLoginKey(Guid.NewGuid()));
        }

        [TestMethod]
        public void User_GetByLoginKey_WithCorrectKey_ReturnsNotNull()
        {
            UserFactory factory = new UserFactory(_Context);
            Assert.IsNotNull(factory.GetByLoginKey(_TestUserLoginKeys[0].Id));
        }

        [TestMethod]
        public void User_GetByLoginKey_WithCorrectKey_ReturnsCorrectUser()
        {
            UserFactory factory = new UserFactory(_Context);
            var tmp = factory.GetByLoginKey(_TestUserLoginKeys[3].Id);
            Assert.AreEqual(_TestUsers[3].Id, tmp.Id);
        }*/
    }
}
